package com.p6spy.engine.wrapper;

import java.sql.SQLException;
import java.sql.Wrapper;

/**
 * 抽象类AbstractWrapper，实现Wrapper接口和P6Proxy接口，
 * 用于创建并管理代理对象以及处理与JDBC Wrapper接口相关的逻辑。
 */
public abstract class AbstractWrapper implements Wrapper, P6Proxy {

    /**
     * 被代理对象引用
     */
    private final Object delegate;

    /**
     * 构造方法，传入被代理的对象实例
     *
     * @param delegate 需要被代理的实际对象
     */
    protected AbstractWrapper(Object delegate) {
        this.delegate = delegate;
    }

    /**
     * 判断给定对象是否是由当前代理工厂创建的代理对象
     *
     * @param obj 需要判断的对象
     * @return 如果是代理对象则返回true，否则返回false
     */
    public static boolean isProxy(final Object obj) {
        return (obj != null && isProxy(obj.getClass()));
    }

    /**
     * 判断给定的类是否为代理类
     *
     * @param clazz 待判断的类
     * @return 若是代理类则返回true，否则返回false
     */
    public static boolean isProxy(final Class<?> clazz) {
        return (clazz != null && P6Proxy.class.isAssignableFrom(clazz));
    }

    /**
     * 根据提供的接口类型解包代理对象。如果此代理或其委托对象实现了指定接口，则返回该接口的一个实例。
     *
     * @param <T>   目标接口类型
     * @param iface 需要转换的目标接口
     * @return 实现了iface接口的对象实例
     * @throws SQLException 当无法转换到目标接口时抛出异常
     */
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        Object result;
        if (iface.isAssignableFrom(getClass())) {
            // 如果代理直接实现了接口或继承了该接口，返回代理本身
            result = this;
        } else if (iface.isAssignableFrom(delegate.getClass())) {
            // 如果被代理对象直接实现了接口或继承了该接口，返回被代理对象
            result = unwrapP6SpyProxy();
        } else if (Wrapper.class.isAssignableFrom(delegate.getClass())) {
            // 如果被代理对象实现了Wrapper接口，则调用其unwrap方法并返回结果
            result = ((Wrapper) unwrapP6SpyProxy()).unwrap(iface);
        } else {
            throw new SQLException("无法将代理对象转换为 " + iface.getName());
        }
        return iface.cast(result);
    }

    /**
     * 检查此代理对象或其底层对象是否直接或间接地实现了指定接口。
     *
     * @param iface 需要检查的接口
     * @return 如果此代理对象或其底层对象实现了iface接口，则返回true；否则返回false
     * @throws SQLException 当检查过程中出现错误时抛出异常
     */
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        if (iface.isAssignableFrom(getClass())) {
            // 如果代理直接实现了接口或继承了该接口，返回true
            return true;
        } else if (iface.isAssignableFrom(delegate.getClass())) {
            // 如果被代理对象直接实现了接口或继承了该接口，返回true
            return true;
        } else if (Wrapper.class.isAssignableFrom(delegate.getClass())) {
            // 如果被代理对象实现了Wrapper接口，返回其isWrapperFor方法的结果
            return ((Wrapper) unwrapP6SpyProxy()).isWrapperFor(iface);
        }
        return false;
    }

    /**
     * 重写equals方法以正确比较代理对象和非代理对象，确保在相等性测试中能够解包P6Spy代理对象。
     *
     * @param obj 另一个对象
     * @return 如果两个对象相等（根据equals方法的约定），则返回true，否则返回false
     */
    @Override
    public boolean equals(Object obj) {
        // 如果传递给equals方法的对象是另一个P6Spy代理对象，则先解包它
        if (obj instanceof P6Proxy) {
            obj = ((P6Proxy) obj).unwrapP6SpyProxy();
        }
        return delegate.equals(obj);
    }

    /**
     * 重写hashCode方法以确保代理对象的哈希码与其底层对象一致。
     *
     * @return 委托对象的哈希码
     */
    @Override
    public int hashCode() {
        return delegate.hashCode();
    }

    /**
     * 返回被代理对象，即原始对象，剥离掉所有P6Spy代理层。
     *
     * @return 被代理的实际对象
     */
    @Override
    public Object unwrapP6SpyProxy() {
        return delegate;
    }

}
